// 在上次打劫完一条街道之后和一圈房屋后，小偷又发现了一个新的可行窃的地区。
// 这个地区只有一个入口，我们称之为“根”。 除了“根”之外，每栋房子有且只有一个“父“房子与之相连。
// 一番侦察之后，聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫，房屋将自动报警。
// 计算在不触动警报的情况下，小偷一晚能够盗取的最高金额。
// 思路1，后序遍历
function rob(root) {
    function dfs(node) {
        if (!node) {
            return 0
        }
        if (!node.left && !node.right) {
            return node.val
        }
        let val1 = node.val
        if (node.left) {
            val1 += dfs(node.left.left) + dfs(node.left.right)
        }
        if (node.right) {
            val1 += dfs(node.right.left) + dfs(node.right.right)
        }
        let val2 = dfs(node.left) + dfs(node.right)
        return Math.max(val1, val2)
    }
    return dfs(root)
}

// 思路2，记忆搜索
// 时间复杂度为On，空间复杂度为Ologn
function rob2(root) {
    let map = new Map
    function dfs(node) {
        if (!node) {
            return 0
        }
        if (!node.left && !node.right) {
            return node.val
        }
        if(map.has(node)) {
            return map.get(node)
        }
        let val1 = node.val
        if (node.left) {
            val1 += dfs(node.left.left) + dfs(node.left.right)
        }
        if (node.right) {
            val1 += dfs(node.right.left) + dfs(node.right.right)
        }
        let val2 = dfs(node.left) + dfs(node.right)
        let result = Math.max(val1, val2)
        map.set(node, result)
        return result
    }
    return dfs(root)
}

// 思路3，动态规划
// 深度递归搜索对每个节点偷与不偷没有做记录，动态规划需要使用动态转移容器来记录状态的变化，
// 可以使用长度为2的数组，记录当前节点偷或者不偷，得到的最大金钱

// 树形dp，基于递归三部曲+动态规划五部曲
// 1. 确定递归函数的参数和返回值, dp数组为长度为2的数组，表示当前节点偷或者不偷，索引0，表示不偷，索引1表示偷，得到的最大金钱，递归的过程中，系统栈会保存每一层递归的参数
// ```ts
// function robTree(node: number): [number, number] {
//     return []
// }
// ```
// 2. 确定终止条件,空节点，偷或不偷，都是0

// ```js
// if(!node) {
//     return [0, 0]
// }
// ```

// 3. 确定遍历顺序，后序遍历，需要通过递归遍历的返回值做下一步计算
// 4. 确定单层递归的逻辑
//     1. 如果偷当前节点，则左右孩子就不能偷,`val + left[0] + right[0]`
//     2. 如果不偷当前节点，则左右孩子可以偷，得到两种情况的最大值，`val2 = Math.max(left[0], left[1]) + Math.max(right[0], right[1]) `
// 5. 举例推导

function rob3(root) {
    function dfs(root) {
        if (!root) {
            return [0, 0]
        }
        let left = dfs(root.left)
        let right = dfs(root.right)
        // 不偷当前节点，偷左节点和右节点
        let val1 = Math.max(...left) + Math.max(...right)
        // 偷当前节点，不偷左右子节点
        let val2 = root.val + left[0] + right[0]
        return [val1, val2]
    }
    return Math.max(...dfs(root))
}

let root = {
    val: 3, 
    left: {
        val: 2,
        right: {
            val: 3
        }
    },
    right: {
        val: 3,
        right: {
            val: 1
        }
    }
}
console.log(rob(root))
console.log(rob2(root))
console.log(rob3(root))